#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>

#define BUFFER_SIZE 1024

 // Print error message and exit
void error(const char* msg) {
    perror(msg);
    exit(1);
}

// Handle a single client connection
void handle_client(int client_fd, const char* www_root) {
    char buffer[BUFFER_SIZE];
    ssize_t n = read(client_fd, buffer, BUFFER_SIZE - 1);
    if (n <= 0) return;
    buffer[n] = '\0';

    // Parse request line: METHOD PATH
    char method[16], path[256];
    if (sscanf(buffer, "%15s %255s", method, path) != 2) return;

    // Only support GET
    if (strcmp(method, "GET") != 0) {
        const char* msg = "HTTP/1.1 501 Not Implemented\r\n"
            "Content-Length: 0\r\n\r\n";
        write(client_fd, msg, strlen(msg));
        return;
    }

    // Default to index.html if root requested
    if (strcmp(path, "/") == 0) {
        strcpy(path, "/index.html");
    }

    // Construct full file path
    char file_path[512];
    snprintf(file_path, sizeof(file_path), "%s%s", www_root, path);

    // Try to open the file
    int fd = open(file_path, O_RDONLY);
    if (fd < 0) {
        // File not found
        const char* not_found = "HTTP/1.1 404 Not Found\r\n"
            "Content-Length: 0\r\n\r\n";
        write(client_fd, not_found, strlen(not_found));
    }
    else {
        // Determine file size
        struct stat st;
        fstat(fd, &st);

        // Send HTTP headers
        char header[BUFFER_SIZE];
        int header_len = snprintf(header, sizeof(header),
            "HTTP/1.1 200 OK\r\n"
            "Content-Length: %ld\r\n"
            "Content-Type: text/html\r\n"
            "\r\n", st.st_size);
        write(client_fd, header, header_len);

        // Send file content
        while ((n = read(fd, buffer, BUFFER_SIZE)) > 0) {
            write(client_fd, buffer, n);
        }
        close(fd);
    }
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <port> <www_root>\n", argv[0]);
        exit(1);
    }
    int port = atoi(argv[1]);
    const char* www_root = argv[2];

    // Create socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) error("socket");

    // Allow address reuse
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // Bind to specified port
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(port);

    if (bind(server_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
        error("bind");

    // Listen for connections
    listen(server_fd, 10);
    printf("Server listening on port %d, serving %s\n", port, www_root);

    // Main loop: accept and handle
    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            perror("accept");
            continue;
        }
        handle_client(client_fd, www_root);
        close(client_fd);
    }

    close(server_fd);
    return 0;
}
